import time
import random
import requests
from xml.etree import ElementTree as ET
import base64
import hashlib
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5


class WxPay:
    APP_ID = None
    APP_SECRET = None
    MCH_ID = None
    openid = None
    notify_url = ""

    @classmethod
    def SHA256WithRSA(self, sign_data: str, private_key: str):
        rsa_key = RSA.importKey(private_key)
        signer = PKCS1_v1_5.new(rsa_key)
        _rand_hash = SHA256.new()
        _rand_hash.update(sign_data.encode('utf-8'))
        signature = signer.sign(_rand_hash)
        return base64.b64encode(signature).decode('utf-8')

    def sign(self):
        sign_Str = "GET\n  /v3/certificates\n  1703127006\n  593BEC0C930BF1AFEB40B4A08C8FB242\n  \n"

    def __init__(self, **kwargs):
        self.APP_ID = kwargs.get("APP_ID")
        self.APP_SECRET = kwargs.get("APP_SECRET")
        self.MCH_ID = kwargs.get("MCH_ID")
        if self.APP_ID is None:
            raise ValueError("请填写 APP_ID")
        if self.APP_SECRET is None:
            raise ValueError("请填写 APP_SECRET")
        if self.MCH_ID is None:
            raise ValueError("请填写 MCH_ID")

    def md5(self, string):
        """
        md5加密
        """
        m = hashlib.md5()
        m.update(string.encode('utf-8'))
        return m.hexdigest()

    def pay(self, **kwargs):
        """
        预支付
        """
        if self.openid is not None:
            pay_info = {}
            pay_info["appid"] = self.APP_ID
            pay_info["mch_id"] = self.MCH_ID
            pay_info["openid"] = self.openid
            pay_info["nonce_str"] = "".join([chr(random.randint(65, 90)) for _ in range(12)]),
            pay_info["sign_type"] = "MD5"
            pay_info["out_trade_no"] = kwargs.get("out_trade_no")
            pay_info["trade_type"] = "JSAPI"
            pay_info["device_info"] = "ymq-text"
            pay_info["notify_url"] = "http://mini.unicorn-go.com:1000/applet/payNotify"
            # 取参
            pay_info["body"] = kwargs.get("body")
            pay_info["detail"] = kwargs.get("detail")
            pay_info["attach"] = kwargs.get("attach")
            pay_info["total_fee"] = kwargs.get("total_fee")  # 总金额
            pay_info["spbill_create_ip"] = kwargs.get("spbill_create_ip")  # 总金额
            info = pay_info
            # 1.1 签名
            #       对字典中的key按照ASCII码从小到大排序
            #       将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051
            #       让stringA和key拼接：stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key
            #       MD5(stringSignTemp)
            #       将密文转换为大写
            #       得到签名 sign
            #       把签名再添加到info中    info['sign'] = sign值
            # 商户密钥key
            pay_key = "ialn3sltm2qwxd2njxx1v2ohb42lfogv"
            #
            temp = "&".join(
                ["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])
            sign = self.md5(temp).upper()
            info['sign'] = sign
            # 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml)

            xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()]))
            prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
            # 1.3 从结果xml中提取 prepay_id

            root = ET.XML(prepay.content.decode('utf-8'))
            prepay_dict = {child.tag: child.text for child in root}
            if prepay_dict["return_code"] == "FAIL":
                raise ValueError(prepay_dict["return_msg"])
            prepay_id = prepay_dict['prepay_id']

            print("prepay_id", prepay_id)
            # ####################### 2.再次签名 #######################
            info_dict = {
                # 小程序的appid
                "appId": self.APP_ID,
                "timeStamp": str(int(time.time())),  # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
                "nonceStr": "".join([chr(random.randint(65, 90)) for _ in range(12)]),  # 随机字符串，长度为32个字符以下。
                "package": 'prepay_id={0}'.format(prepay_id),  # 统一下单接口返回的 prepay_id 参数值，提交格式如：prepay_id=*
                "signType": "MD5",  # 签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
            }
            temp = "&".join(
                ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ])
            sign2 = self.md5(temp).upper()
            info_dict['paySign'] = sign2

            print("info_dict", info_dict)
            return (prepay_dict, prepay_id, info_dict, info)
        else:
            raise ValueError("请先获取openid,调用 get_open_id")

    def refund(self, **kwargs):
        """
        退款
        """
        refund_info = {}
        refund_info["appid"] = self.APP_ID
        refund_info["mch_id"] = self.MCH_ID
        refund_info["out_trade_no"] = kwargs.get("out_trade_no")
        refund_info["total_fee"] = kwargs.get("total_fee")  # 总金额
        refund_info["refund_fee"] = kwargs.get("refund_fee")  # 退款金额
        refund_info["body"] = kwargs.get("body")  # 退款金额
        refund_info["op_user_id"] = self.MCH_ID  # 操作员 与商户号相同
        refund_info["nonce_str"] = "".join([chr(random.randint(65, 90)) for _ in range(12)]),
        refund_info["sign_type"] = "MD5"
        refund_info["trade_type"] = "JSAPI"
        refund_info["device_info"] = "ymq-text"
        refund_info["notify_url"] = "http://mini.unicorn-go.com:1000/applet/payNotify"
        info = refund_info
        # 1.1 签名
        #       对字典中的key按照ASCII码从小到大排序
        #       将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051
        #       让stringA和key拼接：stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key
        #       MD5(stringSignTemp)
        #       将密文转换为大写
        #       得到签名 sign
        #       把签名再添加到info中    info['sign'] = sign值
        # 商户密钥key
        pay_key = "ialn3sltm2qwxd2njxx1v2ohb42lfogv"
        #
        temp = "&".join(
            ["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])
        sign = self.md5(temp).upper()
        info['sign'] = sign
        # 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml)

        xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()]))
        print(xml_string)
        prepay = requests.post('https://api.mch.weixin.qq.com/v3/refund/domestic/refunds',
                               data=xml_string.encode('utf-8'))
        print(prepay.text)
        # 1.3 从结果xml中提取 prepay_id

        root = ET.XML(prepay.content.decode('utf-8'))
        prepay_dict = {child.tag: child.text for child in root}
        if prepay_dict["return_code"] == "FAIL":
            raise ValueError(prepay_dict["return_msg"])
        prepay_id = prepay_dict['prepay_id']

        print("prepay_id", prepay_id)
        # ####################### 2.再次签名 #######################
        info_dict = {
            # 小程序的appid
            "appId": self.APP_ID,
            "timeStamp": str(int(time.time())),  # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
            "nonceStr": "".join([chr(random.randint(65, 90)) for _ in range(12)]),  # 随机字符串，长度为32个字符以下。
            "package": 'prepay_id={0}'.format(prepay_id),  # 统一下单接口返回的 prepay_id 参数值，提交格式如：prepay_id=*
            "signType": "MD5",  # 签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
        }
        temp = "&".join(
            ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ])
        sign2 = self.md5(temp).upper()
        info_dict['paySign'] = sign2
        return (prepay_dict, prepay_id, info_dict, info)

    def get_open_id(self, code):
        """
        获取open_id
        """
        info = {
            "appid": self.APP_ID,
            "secret": self.APP_SECRET,
            "js_code": code,
            "grant_type": "authorization_code"
        }
        response = requests.get(url='https://api.weixin.qq.com/sns/jscode2session', params=info)
        result = response.json()
        if "openid" in result.keys():
            openid = result['openid']
            self.openid = openid
            print("open_id", openid)
            return (True, openid)
        else:
            return (False, result.get("errmsg"))


class WxPayNew:
    APP_ID = None
    APP_SECRET = None
    MCH_ID = None
    openid = None
    notify_url = ""
    pay_url = "https://api.weixin.qq.com/sns/jscode2session"

    def __init__(self, **kwargs):
        self.APP_ID = kwargs.get("APP_ID")
        self.APP_SECRET = kwargs.get("APP_SECRET")
        self.MCH_ID = kwargs.get("MCH_ID")
        if self.APP_ID is None:
            raise ValueError("请填写 APP_ID")
        if self.APP_SECRET is None:
            raise ValueError("请填写 APP_SECRET")
        if self.MCH_ID is None:
            raise ValueError("请填写 MCH_ID")

    def md5(self, string):
        """
        md5加密
        """
        m = hashlib.md5()
        m.update(string.encode('utf-8'))
        return m.hexdigest()

    def get_open_id(self, code):
        """
        获取普通商户open_id
        """
        info = {
            "appid": self.APP_ID,
            "secret": self.APP_SECRET,
            "js_code": code,
            "grant_type": "authorization_code"
        }
        response = requests.get(url=self.pay_url, params=info)
        result = response.json()
        if "openid" in result.keys():
            openid = result['openid']
            self.openid = openid
            return (True, openid)
        else:
            return (False, result.get("errmsg"))

    def pay(self, **kwargs):
        # 取参数
        open_id = kwargs.get("open_id")
        body = kwargs.get("body")
        detail = kwargs.get("detail")
        attach = kwargs.get("attach")
        total_fee = kwargs.get("total_fee")  # 总金额
        description = kwargs.get("description")  # 商品描述
        out_trade_no = kwargs.get("out_trade_no")  # 商户订单号
        notify_url = kwargs.get("notify_url")
        # 组织请求数据
        pay_info = {
            "appid": self.APP_ID,
            "mchid": self.MCH_ID,
            "description": description,
            "out_trade_no": out_trade_no,
            "open_id": open_id,
            "body": body,
            "detail": detail,
            "attach": attach,
            "total_fee": total_fee,
            "notify_url": notify_url,
            "nonce_str": "".join([chr(random.randint(65, 90)) for _ in range(12)]),
            "sign_type": "MD5",
            "trade_type": "JSAPI",
        }


if __name__ == '__main__':
    rsa_pri_key = """-----BEGIN PRIVATE KEY-----
    MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCR2eBgYkRLPZgs
    OFf2/81o5i69/S2rjGic/YW7Clm1mokiYZ1YLDEHH2c7tdvXJrmfLavc4ngruX4y
    Uddn9A2+Ts+B5YbsbRrzOJ9ok6CA0MaVpAYejmaT/bq3e2jBY2rwI7NBzfx7c1H/
    gRNY2kMTGK6l2aI4fg8jxCLt+5jOU9UbvEmwd4VNBcqfUpb9VTukdCfXt9p3jtLB
    ej2bc2CQwReuv8FldhuGzDgHLvRRplXeGQ1i2OwvCguJiZH8mCq4/18336Mf0Xoc
    bG7BmQYATPf+0OOkb1fJ9vDTtgLDB0T1BxiajoUNP5F4RLWer0eIDnF6iO46zwjC
    czasKs3XAgMBAAECggEAEDog7ChSt9pRAW59XvZD6M1fWvT9rU3wX7MXO2Gh7IIW
    itQ4eNRl2giE6FBJ9syQr2SZ7/fkbdzz7O5NHHw7QTasgKPEWK6k5nWgyrZOD7bq
    uUBoC+NFB2cd7IcW9xk0DyaxrXsZAbUpZNd8CxExkEDZQYuOsGFaFt0eE8M/04i2
    L9QPB3WQ3lN+sK8q9AfTBK3yv8MkGI8+00Ihp9TLZ1XqhtvlJSxKs6vLFAcH8rzP
    ZKsSnqM7KGVlv5azkIeFeNLWHIzjepjA3V4ncnbu4nOBiuNGlcNh9++ELmHF+fNk
    EmGO4u+GTgfuaECFtjdeq+DUH8JJ0dPXOEMhGX8p2QKBgQC5yxa3CodwW832dQ+u
    pQaN3J7pZfgUrHK1idGSyGxuehEU+wyjSSwOC1eUGaP4dmj6azmvz5Ng4xPa7Ew3
    TemNz7HGVnx8L+YMYa+cJHEfaWEvByin5nnPgy79Fy6aO49uK1egFGFNSPV7i4Sd
    nnGt/8ye+g65uS2MwSxAN3V/WQKBgQDI9u8G0xFNI3tg7glFTr9yy/WihCZ495BZ
    Bq7MAQWsaNUOR0Rh6/+gDDG2j0m3Yjnk9JFXpuctZGnVyWi1fBGjjW25e8NiyBjz
    BJbpPNrkxJWpdeG51gKaOUgz2YfnooHKrt/3Cn1LO6KrNBPZMw7++NpWWh+aRE6A
    cVWFOUbArwKBgHYLQdBkQS8zNQYc1CzrLuHdRZ4XKmrAMlWDTgNLkhETP10sMJhi
    OjpmHGu9ar/HQ+WvUMSSRxoszIWw0q/ksbpnNpVEh+1DZY+CVVgIk1MY2iVOEBe1
    SLl+qNEm9HYL15JwuUi9CiFXdJjdSRH4BFRADRsI12hK7lTauynF0sJpAoGBAIt/
    fWRLVxMoshgSo7TMePCCy7tH6DzH/BcQIH3Tp9CJ5HrI1Zrzarn6PPfwdscpE+7u
    JYWmgYpszVptOJXhzYyuO6ApXPNQ2qC2atr0Ny3dl9XN0iZhe+T6Jjh3o3MSxu2e
    fjDJwzdZ6gzVVYHLg3lR3J1cadt31tYp1adonshZAoGAWTgi+QE1ts0HwztCw2CV
    3AeKB6wZNfFNYEE4bwZhwWUQMOEjnlwh9IHwjMh5POyFD5BR/YAgihW3/u052/9O
    1w7I18QvqjwSyPRVMXhItT51P5QNsugRRcsCNTHvWdozjgKuIP5pLTK+nwE7PPl3
    UUNybmttjqJCyfndIOUOVlQ=
    -----END PRIVATE KEY-----"""
    result = WxPay.SHA256WithRSA(
        sign_data="GET\n  /v3/certificates\n  1703127006\n  593BEC0C930BF1AFEB40B4A08C8FB242\n  \n",
        private_key=rsa_pri_key)
    print(result)
